package ai;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import model.Connected4Game;
import model.Slot;
import model.State;

public class ABPruningPlayer extends AIPlayer {
	public ABPruningPlayer(Color color, String description, int maxDepth,
			boolean enDepthCheck, BestChoiceType choiceType,
			boolean enReordering, List<HeuristicType> heuristics) {
		super(color, description, maxDepth, enDepthCheck, choiceType, enReordering, heuristics);
	}

	@Override
	public int executeMove() {
		return ABSearch(board);
	}

	private int ABSearch(Slot[][] boardState) {
		ArrayList<Integer> actions = getActions(boardState);
		ArrayList<NodeValue> returnValues = new ArrayList<NodeValue>();
		for (int i = 0; i < actions.size(); i++) {
			returnValues.add(getMinValue(
					result(boardState, actions.get(i), getPlayerSlot()),
					Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, 1));
		}

		/**************************************************************
		 * BEST CHOICHE TYPE *
		 **************************************************************/
		int move;
		
		switch (choiceType) {
		case CENTERED:
			move = getCenteredShortestBestActionFromValues(actions,
					returnValues);
			break;
		case RANDOM:
			move = getRandomBestActionFromValues(actions, returnValues);
			break;
		case GAUSSIAN:
			move = getGaussianBestActionFromValues(actions, returnValues);
			break;
		default:
			move = getCenteredShortestBestActionFromValues(actions,
					returnValues);
			break;
		}

		return move;

	}

	private NodeValue getMaxValue(Slot[][] boardState, float alpha, float beta,
			int actualDepth) {

		State s = Connected4Game.checkGameState(boardState);
		if (!s.equals(State.PLAYING) || actualDepth == maxDepth) {
			float utility = getUtility(s, boardState,getPlayerSlot());
			return new NodeValue(utility, actualDepth);
		}

		// valore arbitrario (-inf)
		NodeValue v = new NodeValue(Float.NEGATIVE_INFINITY, actualDepth);
		for (int col : getActions(boardState)) {
			NodeValue returnValue = getMinValue(
					result(boardState, col, getPlayerSlot()), alpha, beta,
					actualDepth + 1);
			v = returnValue.getValue() > v.getValue() ? returnValue : v;
			if (v.getValue() >= beta) {
				return v;
			} else
				alpha = alpha > v.getValue() ? alpha : v.getValue();
		}

		return v;
	}

	private NodeValue getMinValue(Slot[][] boardState, float alpha, float beta,
			int actualDepth) {

		State s = Connected4Game.checkGameState(boardState);
		if (!s.equals(State.PLAYING) || actualDepth == maxDepth) {
			float utility = getUtility(s, boardState,getOpponentSlot());
			return new NodeValue(utility, actualDepth);
		}

		// valore arbitrario (+inf)
		NodeValue v = new NodeValue(Float.POSITIVE_INFINITY, actualDepth);
		for (int col : getActions(boardState)) {
			NodeValue returnValue = getMaxValue(
					result(boardState, col, getOpponentSlot()), alpha, beta,
					actualDepth + 1);
			v = returnValue.getValue() < v.getValue() ? returnValue : v;
			if (v.getValue() <= alpha) {
				return v;
			} else
				beta = beta < v.getValue() ? beta : v.getValue();
		}

		return v;
	}

}
